package main

import (
	"archive/zip"
	"bytes"
	"context"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/golang-module/carbon/v2"
	"github.com/spf13/viper"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

type Server struct {
	Name               string
	CertificatePath    string
	CertificateKeyPath string
}

type IServer interface {
	Domain() string
	CertExpired() bool
	UpdateCert() error
	Query4DownLoadCert(certificateId string) error
	DownloadCert(certificateId string) error
}

func (s *Server) Domain() string {
	return s.Name
}

func (s *Server) CertExpired() bool {
	//加载PEM格式证书到字节数组
	certPEMBlock, err := os.ReadFile(s.CertificatePath)
	if err != nil {
		logger.Println("本地证书读取失败", err)
		return true
	}
	//获取下一个pem格式证书数据 -----BEGIN CERTIFICATE-----   -----END CERTIFICATE-----
	certDERBlock, _ := pem.Decode(certPEMBlock)
	if certDERBlock == nil {
		logger.Println("本地证书获取失败", err)
		return true
	}
	x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
	if err != nil {
		logger.Println("本地证书解析失败", err)
		return true
	}
	diffDaysLimit := viper.GetInt64("diff-days-limit")
	diffDays := -carbon.CreateFromStdTime(x509Cert.NotAfter).DiffInDays()
	return diffDays < diffDaysLimit
}

func (s *Server) UpdateCert() error {
	// 1.请求签发
	logger.Println("请求签发证书")
	rs, err := ApplyCertificate(s.Name, viper.GetString("email"), "测试签发")
	if err != nil {
		return fmt.Errorf("ApplyCertificateRequest error: %s", err)
	}
	logger.Println("CertificateId:", *rs.Response.CertificateId)

	// 2.3秒后主动触发验证，然后轮寻尝试下载
	<-time.After(time.Second * 3)
	return s.Query4DownLoadCert(*rs.Response.CertificateId)
}

func (s *Server) Query4DownLoadCert(certificateId string) error {
	// 1.主动触发证书验证
	logger.Println("主动触发证书验证", s.Name)
	_, err := CompleteCertificate(certificateId)
	if err != nil {
		return errors.New("CompleteCertificateRequest error: " + err.Error())
	}

	// 2.轮寻一定时间查询证书签发状态
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*time.Duration(viper.GetInt64("timeout")))
	defer cancelFunc()

	i := 1
	for {
		select {
		case <-ctx.Done():
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				return errors.New("证书签发等待超时")
			}
			return errors.New("证书签发失败: " + ctx.Err().Error())
		case <-time.After(time.Second):
			rs, err := DescribeCertificate(certificateId)
			if err != nil {
				return fmt.Errorf("第%d次证书签发查询，异常: %s", i, err)
			}
			logger.Printf("第%d次证书签发查询:", i)
			if rs.Response.Status == nil {
				return errors.New("证书状态查询失败: 未返回有效状态码")
			}
			switch *rs.Response.Status {
			case 1:
				logger.Printf("第%d次证书状态查询: 已通过", i)
				return nil
			case 2:
				if rs.Response.StatusMsg == nil {
					return errors.New("证书状态查询失败: 审核失败")
				} else {
					return errors.New("证书状态查询失败: " + *rs.Response.StatusMsg)
				}
			default:
				if rs.Response.StatusMsg == nil {
					logger.Printf("第%d次证书状态查询: 状态码：%d", i, *rs.Response.Status)
				} else {
					logger.Printf("第%d次证书状态查询: %s", i, *rs.Response.StatusMsg)
				}
			}
			i += 1
		}
	}
}

func (s *Server) DownloadCert(certificateId string) error {
	logger.Println("下载证书")
	rs, err := DownloadCertificate(certificateId)
	if err != nil {
		return errors.New("证书下载失败: " + err.Error())
	}

	logger.Println("解压证书", s.Name)
	base64Decoded, err := base64.StdEncoding.DecodeString(*rs.Response.Content)
	if err != nil {
		return errors.New("证书base64解码失败: " + err.Error())
	}
	// 使用内存读取器来处理解码后的数据，避免创建临时文件
	reader := bytes.NewReader(base64Decoded)

	zipReader, err := zip.NewReader(reader, int64(len(base64Decoded)))
	if err != nil {
		return errors.New("文件流读取失败: " + err.Error())
	}
	nginxFolderFound := false
	var crtFile, keyFile *zip.File

	// 遍历ZIP中的文件和目录
	for _, file := range zipReader.File {
		// 查找Nginx目录
		if file.Name == "Nginx/" && file.FileInfo().IsDir() {
			nginxFolderFound = true
			continue // 继续检查Nginx目录下的文件
		}

		if nginxFolderFound {
			// 检查Nginx目录下的.crt和.key文件
			ext := filepath.Ext(file.Name)
			if (ext == ".crt" || ext == ".key") && !file.FileInfo().IsDir() {
				if ext == ".crt" {
					crtFile = file
				} else if ext == ".key" {
					keyFile = file
				}
			}
		}
	}
	if !nginxFolderFound {
		return errors.New("证书下载异常: 未找到Nginx文件夹")
	}
	if crtFile == nil {
		return errors.New("证书下载异常: 未找到证书文件")
	}
	if keyFile == nil {
		return errors.New("证书下载异常: 未找到证书密钥文件")
	}

	// 重命名并备份原有文件
	bakSuffix := time.Now().Format("20060102150405")
	if err = moveAndBackup(s.CertificatePath, bakSuffix); err != nil {
		return errors.New("证书备份异常: " + err.Error())
	}
	if err = moveAndBackup(s.CertificateKeyPath, bakSuffix); err != nil {
		return errors.New("证书密钥备份异常: " + err.Error())
	}

	// 提取.crt和.key文件到当前目录
	err = ExtractFile(crtFile, s.CertificatePath)
	if err != nil {
		return errors.New("证书保存异常: " + err.Error())
	}
	err = ExtractFile(keyFile, s.CertificateKeyPath)
	if err != nil {
		return errors.New("证书密钥保存异常: " + err.Error())
	}
	logger.Println("证书保存成功", s.Name)
	return nil
}

func moveAndBackup(filePath, suffix string) error {
	if !strings.HasPrefix(filePath, "/") {
		filePath = path.Join(viper.GetString("nginx.root"), filePath)
	}
	if _, err := os.Stat(filePath); err == nil {
		backupFilePath := filePath + ".bak_" + suffix
		if err = os.Rename(filePath, backupFilePath); err != nil {
			return fmt.Errorf("文件重命名失败: %s to %s: %s", filePath, backupFilePath, err.Error())
		}
		logger.Printf("%s 已备份为: %s\n", filePath, backupFilePath)
	}
	return nil
}
